<?php
###
#  Baing PHP 框架 1.0.0
#  http://baing.co/
#
#  Copyright: 白晓阳
#  License: CC BY-NC-SA 4.0
#  https://creativecommons.org/licenses/by-nc-sa/4.0/deed.zh
###

namespace bai;

use bai\feature\ProcessException;
use bai\Response;


abstract class Action extends Baing
{
    public const FAILED = 0;
    public const SUCCEEDED = 1;
    public const DONE = 2;
    public const PROCESSING = 3;
    public const INCOMPLETE = -1;

    public const ORDER_PRE = 30;
    public const ORDER_NORMAL = 50;
    public const ORDER_POST = 70;

    protected static array $context = [];

    public function run()
    {
        throw new ProcessException('action', static::class);
    }

    final protected function next(string|array $output = [], int $status = self::SUCCEEDED)
    {
        $context = [
            'action' => static::class,
            'output' => $output
        ];
        if ($subContext = $this->find('context')) {
            $context['context'] = $subContext;
        }
        self::$context[] = $context;
        if ($output) {
            $sub = $this->find('sub') ? '--' : '';
            self::$logger->info("{$sub}-- Action context: " . json_encode($context));
        }
        return $status;
    }

    final protected function exit(array $context = [], bool $quiet = false)
    {
        if (!$quiet && !$this->find('persistent') && self::$response->status < Response::STATUS_BAD_REQUEST) {
            self::$response->setStatus(Response::STATUS_INVALID);
        }
        return $this->next($context, self::FAILED);
    }

    protected function runAction(string $action, array $runtime = []): string|array
    {
        self::$logger->info("---- Action Start: $action");
        if ($runtime) {
            self::$logger->info('---- Action runtime: ' . json_encode($runtime));
        }
        $runtime['sub'] = ($this->find('sub') ?? 0) + 1;

        $lastContext = self::$context;
        self::$context = [];
        $entry = self::loadClass($action, $runtime);
        if ($entry->run() === self::FAILED) {
            throw new ProcessException('action', $action);
        }
        $result = self::$context[0]['output'] ?? [];
        $this->runtime['context'] = array_merge($this->runtime['context'] ?? [], self::$context);
        self::$context = $lastContext;

        self::$logger->info("---- Action End: $action");
        return $result;
    }
}
